package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.*;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * @since 2023-11-01
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;
    private final LearningRecordMapper recordMapper;

    /**
     * 1、添加课表
     *
     * @param userId    用户ID
     * @param courseIds 课程ID集合
     */
    @Transactional
    @Override
    public void addUserLessons(Long userId, List<Long> courseIds) {
        //1、根据课程ID集合查询课程信息
        List<CourseSimpleInfoDTO> courseInfoList = courseClient.getSimpleInfoList(courseIds);
        if (ObjectUtil.isEmpty(courseInfoList)) {
            //课程不存在，无法处理
            log.error("课程信息不存在，无法添加到课表~");
            return;
        }
        //2、循环遍历课程信息集合，组装LearningLesson数据
        List<LearningLesson> lessons = new ArrayList<>(courseInfoList.size());
        for (CourseSimpleInfoDTO cInfo : courseInfoList) {
            LearningLesson lesson = new LearningLesson();

            //1、填充用户ID和课程ID
            lesson.setUserId(userId);
            lesson.setCourseId(cInfo.getId());

            //2、填充过期时间
            LocalDateTime now = LocalDateTime.now();
            Integer validDuration = cInfo.getValidDuration();
            if (ObjectUtil.isEmpty(validDuration) || validDuration <= 0) {
                continue;
            }
            lesson.setCreateTime(now);
            lesson.setExpireTime(now.plusMonths(validDuration));
            lessons.add(lesson);
        }
        //3、批量新增
        if (ObjectUtil.isNotEmpty(lessons)) {
            saveBatch(lessons);
        }
    }

    /**
     * 2、分页查询我的课表
     * @param query 查询条件
     * @return
     */
    @Override
    public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
        // select * from learning_lesson where user_id = #{userId} order by latest_learn_time desc limit 0,5
        //1、获取当前登录用户
        Long userId = UserContext.getUser();

        //2、分页查询
        Page<LearningLesson> page = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId).page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();
        if(ObjectUtil.isEmpty(records)){
            return PageDTO.empty(page);
        }

        //3、查询课程信息
        Map<Long, CourseSimpleInfoDTO> cInfoMap = getCInfoMap(records);

        //4、封装VO，组装PageDTO并返回
        List<LearningLessonVO> resultList = new ArrayList<>(records.size());
        //4.1、循环records，转换成VoList
        for (LearningLesson record : records) {
            //4.2、拷贝属性至vo
            LearningLessonVO vo = BeanUtils.copyBean(record, LearningLessonVO.class);
            //4.3、获取课程信息，填充到vo
            CourseSimpleInfoDTO cInfo = cInfoMap.get(vo.getCourseId());
            vo.setCourseCoverUrl(cInfo.getCoverUrl());
            vo.setCourseName(cInfo.getName());
            vo.setSections(cInfo.getSectionNum());
            //4.4、放入到集合中
            resultList.add(vo);
        }

        return PageDTO.of(page,resultList);
    }

    /**
     * 将LearningLesson list转为map
     * @param records
     * @return
     */
    private Map<Long, CourseSimpleInfoDTO> getCInfoMap(List<LearningLesson> records) {
        //3.1、获取课程ID集合
        Set<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        //3.2、远程调用查询课程信息
        List<CourseSimpleInfoDTO> courseInfoList = courseClient.getSimpleInfoList(courseIds);
        if(ObjectUtil.isEmpty(courseInfoList)){
            //课程都不存在
            throw new BadRequestException("课程信息不存在");
        }
        //3.3、将list转为map,便于下面的逻辑直接从map中根据courseId获取课程信息
        Map<Long, CourseSimpleInfoDTO> cInfoMap = courseInfoList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, Function.identity()));
        return cInfoMap;
    }

    /**
     * 3、查询我正在学习的课程
     * @return
     */
    @Override
    public LearningLessonVO queryMyCurrentLesson() {
        // 1.获取当前登录的用户
        Long userId = UserContext.getUser();
        // 2.查询正在学习的课程 select * from xx where user_id = #{userId} AND status = 1 order by latest_learn_time limit 1
        LearningLesson lesson = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        if (lesson == null) {
            return null;
        }
        // 3.拷贝PO基础属性到VO
        LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);
        // 4.查询课程信息
        CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if (cInfo == null) {
            throw new BadRequestException("课程不存在");
        }
        vo.setCourseName(cInfo.getName());
        vo.setCourseCoverUrl(cInfo.getCoverUrl());
        vo.setSections(cInfo.getSectionNum());
        // 5.统计课表中的课程数量 select count(1) from xxx where user_id = #{userId}
        Integer courseAmount = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .count();
        vo.setCourseAmount(courseAmount);
        // 6.查询小节信息
        List<CataSimpleInfoDTO> cataInfos =
                catalogueClient.batchQueryCatalogue(CollUtils.singletonList(lesson.getLatestSectionId()));
        if (!CollUtils.isEmpty(cataInfos)) {
            CataSimpleInfoDTO cataInfo = cataInfos.get(0);
            vo.setLatestSectionName(cataInfo.getName());
            vo.setLatestSectionIndex(cataInfo.getCIndex());
        }
        return vo;
    }

    /**
     * 4、查询指定课程信息（Day2 - 作业）
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonVO queryLessonByCourseId(Long courseId) {
        // 1.获取当前登录用户
        Long userId = UserContext.getUser();
        // 2.查询课程信息 select * from xx where user_id = #{userId} AND course_id = #{courseId}
        LearningLesson lesson = getLearningLesson(userId,courseId);
        if(lesson == null){
            return null;
        }
        // 3.处理VO
        return BeanUtils.copyBean(lesson, LearningLessonVO.class);
    }

    /**
     * 5、删除指定课程信息（Day2 - 作业）
     * @param userId
     * @param courseId
     */
    @Override
    public void deleteCourseFromLesson(Long userId, Long courseId) {
        // 1.获取当前登录用户
        if (userId == null) {
            userId = UserContext.getUser();
        }
        // 2.删除课程
        remove(buildUserIdAndCourseIdWrapper(userId, courseId));
    }

    /**
     * 6、统计课程学习人数（Day2 - 作业）
     * @param courseId
     * @return
     */
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        // select count(1) from xx where course_id = #{cc} AND status in (0, 1, 2)
        return lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .in(LearningLesson::getStatus,
                        LessonStatus.NOT_BEGIN.getValue(),
                        LessonStatus.LEARNING.getValue(),
                        LessonStatus.FINISHED.getValue())
                .count();
    }

    /**
     * 7、校验当前课程是否已经报名（Day2 - 作业）
     * @param courseId
     * @return
     */
    @Override
    public Long isLessonValid(Long courseId) {
        // 1.获取登录用户
        Long userId = UserContext.getUser();
        if (userId == null) {
            return null;
        }
        // 2.查询课程
        LearningLesson lesson = getOne(buildUserIdAndCourseIdWrapper(userId, courseId));
        if (lesson == null || LessonStatus.EXPIRED.equals(lesson.getStatus())) {
            return null;
        }
        return lesson.getId();
    }

    /**
     * 8、根据课程ID和用户ID查询课表信息
     * @param courseId
     * @param userId
     * @return
     */
    @Override
    public LearningLesson getLearningLesson(Long userId,Long courseId) {
        LearningLesson lesson = getOne(buildUserIdAndCourseIdWrapper(userId, courseId));
        return lesson;
    }

    /**
     * 9、创建学习计划 (Day3- 课堂)
     *
     * @param planDTO
     */
    @Override
    public void createPlans(LearningPlanDTO planDTO) {
        //1、查询课表
        LearningLesson learningLesson = getLearningLesson(UserContext.getUser(), planDTO.getCourseId());
        AssertUtils.isNotNull(learningLesson, "课表信息不存在");

        //2、修改数据
        LearningLesson lesson = new LearningLesson();
        lesson.setId(learningLesson.getId());
        lesson.setWeekFreq(planDTO.getFreq());
        if(learningLesson.getPlanStatus() == PlanStatus.NO_PLAN){
            lesson.setPlanStatus(PlanStatus.PLAN_RUNNING);
        }
        updateById(lesson);
    }

    /**
     * 10、查询我的学习计划 (Day3- 课堂)
     *
     * @param query
     * @return
     */
    @Override
    public LearningPlanPageVO queryMyPlans(PageQuery query) {
        LearningPlanPageVO vo =  new LearningPlanPageVO();
        // select * from learning_lesson where user_id = #{} and plan_status = 1
        // and status  in (0,1) and latest_learn_time between #{begin} and {end}
        //1、获取登录用户
        Long userId = UserContext.getUser();

        //2、获取本周起始时间
        LocalDateTime begin = DateUtils.getWeekBeginTime(LocalDate.now());
        LocalDateTime end = DateUtils.getWeekEndTime(LocalDate.now());

        //3、分页查询课表
        Page<LearningLesson> pageResult = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .between(LearningLesson::getLatestLearnTime, begin, end)
                .page(query.toMpPage("latest_learn_time", false));

        List<LearningLesson> records = pageResult.getRecords();
        if(ObjectUtil.isEmpty(records)){
            vo.emptyPage(pageResult);
        }

        //4、查询课程数据
        Map<Long, CourseSimpleInfoDTO> cInfoMap = getCInfoMap(records);

        //5、统计学习记录中的本周学习小节数
        QueryWrapper<LearningRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id",userId)
                    .eq("finished", true)
                    .between("finish_time",begin,end)
                    .select("lesson_id as id,count(1) as num")
                    .groupBy("lesson_id");

        List<Map<String, Object>> maps = this.recordMapper.selectMaps(queryWrapper);
        Map<Long, Integer> learnedSectionNumMap = maps.stream().collect(Collectors.toMap(m -> (Long) m.get("id"), m -> Integer.valueOf(((Long) m.get("num")).toString())));
        //6、循环课表数据
        ArrayList<LearningPlanVO> voList = new ArrayList<>(records.size());
        for (LearningLesson record : records) {
            //6.1、填充课表数据
            LearningPlanVO learningPlanVO = BeanUtils.copyBean(record, LearningPlanVO.class);
            //6.2、填充课程数据
            CourseSimpleInfoDTO courseSimpleInfoDTO = cInfoMap.get(record.getCourseId());
            if(ObjectUtil.isNotEmpty(courseSimpleInfoDTO)){
                learningPlanVO.setCourseName(courseSimpleInfoDTO.getName());
                learningPlanVO.setSections(courseSimpleInfoDTO.getSectionNum());
            }
            //6.3、填充本周已学习小节数
            learningPlanVO.setWeekLearnedSections(learnedSectionNumMap.getOrDefault(learningPlanVO.getId(),0));

            //6.4、添加到LearningPlanVO List
            voList.add(learningPlanVO);
        }

        //7、计算本周计划总小节数
        int planSum = voList.stream().mapToInt(LearningPlanVO::getWeekFreq).sum();
        //8、计算本周已学习总小节数
        int sum = voList.stream().mapToInt(LearningPlanVO::getWeekLearnedSections).sum();

        //planSum = voList.stream().flatMapToInt(v -> IntStream.of(v.getWeekFreq())).sum();
        //planSum = voList.stream().map(v -> v.getWeekFreq()).reduce(Integer::sum).get();
        //planSum = voList.stream().collect(Collectors.summingInt(LearningPlanVO::getWeekFreq));

        //9、封装返回
        vo.setWeekTotalPlan(planSum);
        vo.setWeekFinished(sum);
        return vo.pageInfo(pageResult.getTotal(),pageResult.getPages(),voList);
    }


    private LambdaQueryWrapper<LearningLesson> buildUserIdAndCourseIdWrapper(Long userId, Long courseId) {
        LambdaQueryWrapper<LearningLesson> queryWrapper = new QueryWrapper<LearningLesson>()
                .lambda()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId);
        return queryWrapper;
    }
}
